/**
******************************************************************************
 *
 * @file rwnx_radar.c
 *
 * @brief Functions to handle radar detection
 * Radar detection is copied (and adapted) from ath driver source code.
 *
 * Copyright (c) 2012 Neratec Solutions AG
 * Copyright (C) RivieraWaves 2015-2019
 *
 ******************************************************************************
 */
#include <linux/list.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <net/mac80211.h>

#include "rwnx_radar.h"
#include "rwnx_defs.h"
#include "rwnx_msg_tx.h"
#include "rwnx_events.h"
#include "rwnx_compat.h"

/*
 * tolerated deviation of radar time stamp in usecs on both sides
 * TODO: this might need to be HW-dependent
 */
#define PRI_TOLERANCE  16

/**
 * struct radar_types - contains array of patterns defined for one DFS domain
 * @domain: DFS regulatory domain
 * @num_radar_types: number of radar types to follow
 * @radar_types: radar types array
 */
struct radar_types {
	enum nl80211_dfs_regions region;
	u32 num_radar_types;
	const struct radar_detector_specs *spec_riu;
	const struct radar_detector_specs *spec_fcu;
};

/**
 * Type of radar waveform:
 * RADAR_WAVEFORM_SHORT : waveform defined by
 *  - pulse width
 *  - pulse interval in a burst (pri)
 *  - number of pulses in a burst (ppb)
 *
 * RADAR_WAVEFORM_WEATHER :
 *   same than SHORT except that ppb is dependent of pri
 *
 * RADAR_WAVEFORM_INTERLEAVED :
 *   same than SHORT except there are several value of pri (interleaved)
 *
 * RADAR_WAVEFORM_LONG :
 *
 */
enum radar_waveform_type {
	RADAR_WAVEFORM_SHORT,
	RADAR_WAVEFORM_WEATHER,
	RADAR_WAVEFORM_INTERLEAVED,
	RADAR_WAVEFORM_LONG
};

/**
 * struct radar_detector_specs - detector specs for a radar pattern type
 * @type_id: pattern type, as defined by regulatory
 * @width_min: minimum radar pulse width in [us]
 * @width_max: maximum radar pulse width in [us]
 * @pri_min: minimum pulse repetition interval in [us] (including tolerance)
 * @pri_max: minimum pri in [us] (including tolerance)
 * @num_pri: maximum number of different pri for this type
 * @ppb: pulses per bursts for this type
 * @ppb_thresh: number of pulses required to trigger detection
 * @max_pri_tolerance: pulse time stamp tolerance on both sides [us]
 * @type: Type of radar waveform
 */
struct radar_detector_specs {
	u8 type_id;
	u8 width_min;
	u8 width_max;
	u16 pri_min;
	u16 pri_max;
	u8 num_pri;
	u8 ppb;
	u8 ppb_thresh;
	u8 max_pri_tolerance;
	enum radar_waveform_type type;
};


/* percentage on ppb threshold to trigger detection */
#define MIN_PPB_THRESH  50
#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100)
#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF)

/* width tolerance */
#define WIDTH_TOLERANCE 2
#define WIDTH_LOWER(X) (X)
#define WIDTH_UPPER(X) (X)

#define ETSI_PATTERN_SHORT(ID, WMIN, WMAX, PMIN, PMAX, PPB)             \
	{                                                                   \
		ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
			(PRF2PRI(PMAX) - PRI_TOLERANCE),                            \
			(PRF2PRI(PMIN) + PRI_TOLERANCE), 1, PPB,                    \
			PPB_THRESH(PPB), PRI_TOLERANCE,  RADAR_WAVEFORM_SHORT       \
			}

#define ETSI_PATTERN_INTERLEAVED(ID, WMIN, WMAX, PMIN, PMAX, PRFMIN, PRFMAX, PPB) \
	{                                                                   \
		ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),                       \
			(PRF2PRI(PMAX) * PRFMIN- PRI_TOLERANCE),                    \
			(PRF2PRI(PMIN) * PRFMAX + PRI_TOLERANCE),                   \
			PRFMAX, PPB * PRFMAX,                                       \
			PPB_THRESH(PPB), PRI_TOLERANCE, RADAR_WAVEFORM_INTERLEAVED  \
			}

/* radar types as defined by ETSI EN-301-893 v1.7.1 */
static const struct radar_detector_specs etsi_radar_ref_types_v17_riu[] = {
	ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
	ETSI_PATTERN_SHORT(1,  0, 10,  200, 1000, 10),
	ETSI_PATTERN_SHORT(2,  0, 22,  200, 1600, 15),
	ETSI_PATTERN_SHORT(3,  0, 22, 2300, 4000, 25),
	ETSI_PATTERN_SHORT(4, 20, 38, 2000, 4000, 20),
	ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
	ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
};

static const struct radar_detector_specs etsi_radar_ref_types_v17_fcu[] = {
	ETSI_PATTERN_SHORT(0,  0,  8,  700,  700, 18),
	ETSI_PATTERN_SHORT(1,  0,  8,  200, 1000, 10),
	ETSI_PATTERN_SHORT(2,  0, 16,  200, 1600, 15),
	ETSI_PATTERN_SHORT(3,  0, 16, 2300, 4000, 25),
	ETSI_PATTERN_SHORT(4, 20, 34, 2000, 4000, 20),
	ETSI_PATTERN_INTERLEAVED(5,  0,  8,  300,  400, 2, 3, 10),
	ETSI_PATTERN_INTERLEAVED(6,  0,  8,  400, 1200, 2, 3, 15),
};

static const struct radar_types etsi_radar_types_v17 = {
	.region          = NL80211_DFS_ETSI,
	.num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v17_riu),
	.spec_riu        = etsi_radar_ref_types_v17_riu,
	.spec_fcu        = etsi_radar_ref_types_v17_fcu,
};

#define FCC_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB, TYPE) \
	{                                                           \
		ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX),               \
			PMIN - PRI_TOLERANCE,                               \
			PMAX * PRF + PRI_TOLERANCE, PRF, PPB * PRF,         \
			PPB_THRESH(PPB), PRI_TOLERANCE, TYPE                \
			}

static const struct radar_detector_specs fcc_radar_ref_types_riu[] = {
	FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
	FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(3,  6,  20,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(4, 10,  28,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(5, 50, 110, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
	FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
};

static const struct radar_detector_specs fcc_radar_ref_types_fcu[] = {
	FCC_PATTERN(0,  0,   8, 1428, 1428, 1,  18, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(1,  0,   8,  518, 3066, 1, 102, RADAR_WAVEFORM_WEATHER),
	FCC_PATTERN(2,  0,   8,  150,  230, 1,  23, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(3,  6,  12,  200,  500, 1,  16, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(4, 10,  22,  200,  500, 1,  12, RADAR_WAVEFORM_SHORT),
	FCC_PATTERN(5, 50, 104, 1000, 2000, 1,   8, RADAR_WAVEFORM_LONG),
	FCC_PATTERN(6,  0,   8,  333,  333, 1,   9, RADAR_WAVEFORM_SHORT),
};

static const struct radar_types fcc_radar_types = {
	.region          = NL80211_DFS_FCC,
	.num_radar_types = ARRAY_SIZE(fcc_radar_ref_types_riu),
	.spec_riu        = fcc_radar_ref_types_riu,
	.spec_fcu        = fcc_radar_ref_types_fcu,
};

#define JP_PATTERN FCC_PATTERN
static const struct radar_detector_specs jp_radar_ref_types_riu[] = {
	JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(1,  2,   8, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(3,  0,   8, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(5,  6,  20,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(6, 10,  28,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(7, 50, 110, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
	JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
};

static const struct radar_detector_specs jp_radar_ref_types_fcu[] = {
	JP_PATTERN(0,  0,   8, 1428, 1428, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(1,  2,   6, 3846, 3846, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(2,  0,   8, 1388, 1388, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(3,  2,   2, 4000, 4000, 1, 18, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(4,  0,   8,  150,  230, 1, 23, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(5,  6,  12,  200,  500, 1, 16, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(6, 10,  22,  200,  500, 1, 12, RADAR_WAVEFORM_SHORT),
	JP_PATTERN(7, 50, 104, 1000, 2000, 1,  8, RADAR_WAVEFORM_LONG),
	JP_PATTERN(8,  0,   8,  333,  333, 1,  9, RADAR_WAVEFORM_SHORT),
};

static const struct radar_types jp_radar_types = {
	.region          = NL80211_DFS_JP,
	.num_radar_types = ARRAY_SIZE(jp_radar_ref_types_riu),
	.spec_riu        = jp_radar_ref_types_riu,
	.spec_fcu        = jp_radar_ref_types_fcu,
};

static const struct radar_types *dfs_domains[] = {
	&etsi_radar_types_v17,
	&fcc_radar_types,
	&jp_radar_types,
};


/**
 * struct pri_sequence - sequence of pulses matching one PRI
 * @head: list_head
 * @pri: pulse repetition interval (PRI) in usecs
 * @dur: duration of sequence in usecs
 * @count: number of pulses in this sequence
 * @count_falses: number of not matching pulses in this sequence
 * @first_ts: time stamp of first pulse in usecs
 * @last_ts: time stamp of last pulse in usecs
 * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur)
 * @ppb_thresh: Number of pulses to validate detection
 *              (need for weather radar whose value depends of pri)
 */
struct pri_sequence {
	struct list_head head;
	u32 pri;
	u32 dur;
	u32 count;
	u32 count_falses;
	u64 first_ts;
	u64 last_ts;
	u64 deadline_ts;
	u8 ppb_thresh;
};


/**
 * struct pulse_elem - elements in pulse queue
 * @ts: time stamp in usecs
 */
struct pulse_elem {
	struct list_head head;
	u64 ts;
};

/**
 * struct pri_detector - PRI detector element for a dedicated radar type
 * @head:
 * @rs: detector specs for this detector element
 * @last_ts: last pulse time stamp considered for this element in usecs
 * @sequences: list_head holding potential pulse sequences
 * @pulses: list connecting pulse_elem objects
 * @count: number of pulses in queue
 * @max_count: maximum number of pulses to be queued
 * @window_size: window size back from newest pulse time stamp in usecs
 * @freq:
 */
struct pri_detector {
	struct list_head head;
	const struct radar_detector_specs *rs;
	u64 last_ts;
	struct list_head sequences;
	struct list_head pulses;
	u32 count;
	u32 max_count;
	u32 window_size;
	struct pri_detector_ops *ops;
	u16 freq;
};

/**
 * struct pri_detector_ops - PRI detector ops (dependent of waveform type)
 * @init : Initialize pri_detector structure
 * @add_pulse : Add a pulse to the pri-detector
 * @reset_on_pri_overflow : Should the pri_detector be resetted when pri overflow
 */
struct pri_detector_ops {
	void (*init)(struct pri_detector *pde);
	struct pri_sequence * (*add_pulse)(struct pri_detector *pde, u16 len, u64 ts, u16 pri);
	int reset_on_pri_overflow;
};


/******************************************************************************
 * PRI (pulse repetition interval) sequence detection
 *****************************************************************************/
/**
 * Singleton Pulse and Sequence Pools
 *
 * Instances of pri_sequence and pulse_elem are kept in singleton pools to
 * reduce the number of dynamic allocations. They are shared between all
 * instances and grow up to the peak number of simultaneously used objects.
 *
 * Memory is freed after all references to the pools are released.
 */
static u32 singleton_pool_references;
static LIST_HEAD(pulse_pool);
static LIST_HEAD(pseq_pool);
static DEFINE_SPINLOCK(pool_lock);

static void pool_register_ref(void)
{
	spin_lock_bh(&pool_lock);
	singleton_pool_references++;
	spin_unlock_bh(&pool_lock);
}

static void pool_deregister_ref(void)
{
	spin_lock_bh(&pool_lock);
	singleton_pool_references--;
	if (singleton_pool_references == 0) {
		/* free singleton pools with no references left */
		struct pri_sequence *ps, *ps0;
		struct pulse_elem *p, *p0;

		list_for_each_entry_safe(p, p0, &pulse_pool, head) {
			list_del(&p->head);
			kfree(p);
		}
		list_for_each_entry_safe(ps, ps0, &pseq_pool, head) {
			list_del(&ps->head);
			kfree(ps);
		}
	}
	spin_unlock_bh(&pool_lock);
}

static void pool_put_pulse_elem(struct pulse_elem *pe)
{
	spin_lock_bh(&pool_lock);
	list_add(&pe->head, &pulse_pool);
	spin_unlock_bh(&pool_lock);
}

static void pool_put_pseq_elem(struct pri_sequence *pse)
{
	spin_lock_bh(&pool_lock);
	list_add(&pse->head, &pseq_pool);
	spin_unlock_bh(&pool_lock);
}

static struct pri_sequence *pool_get_pseq_elem(void)
{
	struct pri_sequence *pse = NULL;
	spin_lock_bh(&pool_lock);
	if (!list_empty(&pseq_pool)) {
		pse = list_first_entry(&pseq_pool, struct pri_sequence, head);
		list_del(&pse->head);
	}
	spin_unlock_bh(&pool_lock);

	if (pse == NULL) {
		pse = kmalloc(sizeof(*pse), GFP_ATOMIC);
	}

	return pse;
}

static struct pulse_elem *pool_get_pulse_elem(void)
{
	struct pulse_elem *pe = NULL;
	spin_lock_bh(&pool_lock);
	if (!list_empty(&pulse_pool)) {
		pe = list_first_entry(&pulse_pool, struct pulse_elem, head);
		list_del(&pe->head);
	}
	spin_unlock_bh(&pool_lock);
	return pe;
}

static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde)
{
	struct list_head *l = &pde->pulses;
	if (list_empty(l))
		return NULL;
	return list_entry(l->prev, struct pulse_elem, head);
}

static bool pulse_queue_dequeue(struct pri_detector *pde)
{
	struct pulse_elem *p = pulse_queue_get_tail(pde);
	if (p != NULL) {
		list_del_init(&p->head);
		pde->count--;
		/* give it back to pool */
		pool_put_pulse_elem(p);
	}
	return (pde->count > 0);
}

/**
 * pulse_queue_check_window - remove pulses older than window
 * @pde: pointer on pri_detector
 *
 *  dequeue pulse that are too old.
 */
static
void pulse_queue_check_window(struct pri_detector *pde)
{
	u64 min_valid_ts;
	struct pulse_elem *p;

	/* there is no delta time with less than 2 pulses */
	if (pde->count < 2)
		return;

	if (pde->last_ts <= pde->window_size)
		return;

	min_valid_ts = pde->last_ts - pde->window_size;
	while ((p = pulse_queue_get_tail(pde)) != NULL) {
		if (p->ts >= min_valid_ts)
			return;
		pulse_queue_dequeue(pde);
	}
}

/**
 * pulse_queue_enqueue - Queue one pulse
 * @pde: pointer on pri_detector
 *
 * Add one pulse to the list. If the maximum number of pulses
 * if reached, remove oldest one.
 */
static
bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts)
{
	struct pulse_elem *p = pool_get_pulse_elem();
	if (p == NULL) {
		p = kmalloc(sizeof(*p), GFP_ATOMIC);
		if (p == NULL) {
			 return false;
		}
	}
	INIT_LIST_HEAD(&p->head);
	p->ts = ts;
	list_add(&p->head, &pde->pulses);
	pde->count++;
	pde->last_ts = ts;
	pulse_queue_check_window(pde);
	if (pde->count >= pde->max_count)
		pulse_queue_dequeue(pde);

	return true;
}


/***************************************************************************
 * Short waveform
 **************************************************************************/
/**
 * pde_get_multiple() - get number of multiples considering a given tolerance
 * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise
 */
static
u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance)
{
	u32 remainder;
	u32 factor;
	u32 delta;

	if (fraction == 0)
		return 0;

	delta = (val < fraction) ? (fraction - val) : (val - fraction);

	if (delta <= tolerance)
		/* val and fraction are within tolerance */
		return 1;

	factor = val / fraction;
	remainder = val % fraction;
	if (remainder > tolerance) {
		/* no exact match */
		if ((fraction - remainder) <= tolerance)
			/* remainder is within tolerance */
			factor++;
		else
			factor = 0;
	}
	return factor;
}

/**
 * pde_short_create_sequences - create_sequences function for
 *                              SHORT/WEATHER/INTERLEAVED radar waveform
 * @pde: pointer on pri_detector
 * @ts: timestamp of the pulse
 * @min_count: Minimum number of pulse to be present in the sequence.
 *             (With this pulse there is already a sequence with @min_count
 *              pulse, so if we can't create a sequence with more pulse don't
 *              create it)
 * @return: false if an error occured (memory allocation) true otherwise
 *
 * For each pulses queued check if we can create a sequence with
 * pri = (ts - pulse_queued.ts) which contains more than @min_count pulses.
 *
 */
static
bool pde_short_create_sequences(struct pri_detector *pde,
								u64 ts, u32 min_count)
{
	struct pulse_elem *p;
	u16 pulse_idx = 0;

	list_for_each_entry(p, &pde->pulses, head) {
		struct pri_sequence ps, *new_ps;
		struct pulse_elem *p2;
		u32 tmp_false_count;
		u64 min_valid_ts;
		u32 delta_ts = ts - p->ts;
		pulse_idx++;

		if (delta_ts < pde->rs->pri_min)
			/* ignore too small pri */
			continue;

		if (delta_ts > pde->rs->pri_max)
			/* stop on too large pri (sorted list) */
			break;

		/* build a new sequence with new potential pri */
		ps.count = 2;
		ps.count_falses = pulse_idx - 1;
		ps.first_ts = p->ts;
		ps.last_ts = ts;
		ps.pri = ts - p->ts;
		ps.dur = ps.pri * (pde->rs->ppb - 1)
			+ 2 * pde->rs->max_pri_tolerance;

		p2 = p;
		tmp_false_count = 0;
		if (ps.dur > ts)
			min_valid_ts = 0;
		else
			min_valid_ts = ts - ps.dur;
		/* check which past pulses are candidates for new sequence */
		list_for_each_entry_continue(p2, &pde->pulses, head) {
			u32 factor;
			if (p2->ts < min_valid_ts)
				/* stop on crossing window border */
				break;
			/* check if pulse match (multi)PRI */
			factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri,
									  pde->rs->max_pri_tolerance);
			if (factor > 0) {
				ps.count++;
				ps.first_ts = p2->ts;
				/*
				 * on match, add the intermediate falses
				 * and reset counter
				 */
				ps.count_falses += tmp_false_count;
				tmp_false_count = 0;
			} else {
				/* this is a potential false one */
				tmp_false_count++;
			}
		}
		if (ps.count <= min_count) {
			/* did not reach minimum count, drop sequence */
			continue;
		}
		/* this is a valid one, add it */
		ps.deadline_ts = ps.first_ts + ps.dur;
		if (pde->rs->type == RADAR_WAVEFORM_WEATHER) {
			ps.ppb_thresh = 19000000 / (360 * ps.pri);
			ps.ppb_thresh = PPB_THRESH(ps.ppb_thresh);
		} else {
			ps.ppb_thresh = pde->rs->ppb_thresh;
		}

		new_ps = pool_get_pseq_elem();
		if (new_ps == NULL) {
			return false;
		}
		memcpy(new_ps, &ps, sizeof(ps));
		INIT_LIST_HEAD(&new_ps->head);
		list_add(&new_ps->head, &pde->sequences);
	}
	return true;
}

/**
 * pde_short_add_to_existing_seqs - add_to_existing_seqs function for
 *                                  SHORT/WEATHER/INTERLEAVED radar waveform
 * @pde: pointer on pri_detector
 * @ts: timestamp of the pulse
 *
 * Check all sequemces created for this pde.
 *  - If the sequence is too old delete it.
 *  - Else if the delta with the previous pulse match the pri of the sequence
 *    add the pulse to this sequence. If the pulse cannot be added it is added
 *    to the false pulses for this sequence
 *
 * @return the length of the longest sequence in which the pulse has been added
 */
static
u32 pde_short_add_to_existing_seqs(struct pri_detector *pde, u64 ts)
{
	u32 max_count = 0;
	struct pri_sequence *ps, *ps2;
	list_for_each_entry_safe(ps, ps2, &pde->sequences, head) {
		u32 delta_ts;
		u32 factor;

		/* first ensure that sequence is within window */
		if (ts > ps->deadline_ts) {
			list_del_init(&ps->head);
			pool_put_pseq_elem(ps);
			continue;
		}

		delta_ts = ts - ps->last_ts;
		factor = pde_get_multiple(delta_ts, ps->pri,
								  pde->rs->max_pri_tolerance);

		if (factor > 0) {
			ps->last_ts = ts;
			ps->count++;

			if (max_count < ps->count)
				max_count = ps->count;
		} else {
			ps->count_falses++;
		}
	}
	return max_count;
}


/**
 * pde_short_check_detection - check_detection function for
 *                             SHORT/WEATHER/INTERLEAVED radar waveform
 * @pde: pointer on pri_detector
 *
 * Check all sequemces created for this pde.
 *  - If a sequence contains more pulses than the threshold and more matching
 *    that false pulses.
 *
 * @return The first complete sequence, and NULL if no sequence is complete.
 */
static
struct pri_sequence *pde_short_check_detection(struct pri_detector *pde)
{
	struct pri_sequence *ps;

	if (list_empty(&pde->sequences))
		return NULL;

	list_for_each_entry(ps, &pde->sequences, head) {
		/*
		 * we assume to have enough matching confidence if we
		 * 1) have enough pulses
		 * 2) have more matching than false pulses
		 */
		if ((ps->count >= ps->ppb_thresh) &&
			(ps->count * pde->rs->num_pri > ps->count_falses)) {
			return ps;
		}
	}
	return NULL;
}

/**
 * pde_short_init - init function for
 *                  SHORT/WEATHER/INTERLEAVED radar waveform
 * @pde: pointer on pri_detector
 *
 * Initialize pri_detector window size to the maximun size of one burst
 * for the radar specification associated.
 */
static
void pde_short_init(struct pri_detector *pde)
{
	pde->window_size = pde->rs->pri_max * pde->rs->ppb * pde->rs->num_pri;
	pde->max_count = pde->rs->ppb * 2;
}

static void pri_detector_reset(struct pri_detector *pde, u64 ts);
/**
 *  pde_short_add_pulse - Add pulse to a pri_detector for
 *                        SHORT/WEATHER/INTERLEAVED radar waveform
 *
 * @pde : pointer on pri_detector
 * @len : width of the pulse
 * @ts  : timestamp of the pulse received
 * @pri : Delta in us with the previous pulse.
 *        (0 means that delta in bigger than 65535 us)
 *
 * Process on pulse within this pri_detector
 * - First try to add it to existing sequence
 * - Then try to create a new and longest sequence
 * - Check if this pulse complete a sequence
 * - If not save this pulse in the list
 */
static
struct pri_sequence *pde_short_add_pulse(struct pri_detector *pde,
										 u16 len, u64 ts, u16 pri)
{
	u32 max_updated_seq;
	struct pri_sequence *ps;
	const struct radar_detector_specs *rs = pde->rs;

	if (pde->count == 0) {
		/* This is the first pulse after reset, no need to check sequences */
		pulse_queue_enqueue(pde, ts);
		return NULL;
	}

	if ((ts - pde->last_ts) < rs->max_pri_tolerance) {
		/* if delta to last pulse is too short, don't use this pulse */
		return NULL;
	}

	max_updated_seq = pde_short_add_to_existing_seqs(pde, ts);

	if (!pde_short_create_sequences(pde, ts, max_updated_seq)) {
		pri_detector_reset(pde, ts);
		return NULL;
	}

	ps = pde_short_check_detection(pde);

	if (ps == NULL)
		pulse_queue_enqueue(pde, ts);

	return ps;
}



/**
 * pri detector ops to detect short radar waveform
 * A Short waveform is defined by :
 *   The width of pulses.
 *   The interval between two pulses inside a burst (called pri)
 *   (some waveform may have or 2/3 interleaved pri)
 *   The number of pulses per burst (ppb)
 */
static struct pri_detector_ops pri_detector_short = {
	.init = pde_short_init,
	.add_pulse = pde_short_add_pulse,
	.reset_on_pri_overflow = 1,
};


/***************************************************************************
 * Long waveform
 **************************************************************************/
#define LONG_RADAR_DURATION 12000000
#define LONG_RADAR_BURST_MIN_DURATION (12000000 / 20)
#define LONG_RADAR_MAX_BURST 20

/**
 * pde_long_init - init function for LONG radar waveform
 * @pde: pointer on pri_detector
 *
 * Initialize pri_detector window size to the long waveform radar
 * waveform (ie. 12s) and max_count
 */
static
void pde_long_init(struct pri_detector *pde)
{
	pde->window_size = LONG_RADAR_DURATION;
	pde->max_count = LONG_RADAR_MAX_BURST; /* only count burst not pulses */
}


/**
 *  pde_long_add_pulse - Add pulse to a pri_detector for
 *                       LONG radar waveform
 *
 * @pde : pointer on pri_detector
 * @len : width of the pulse
 * @ts  : timestamp of the pulse received
 * @pri : Delta in us with the previous pulse.
 *
 *
 * For long pulse we only handle one sequence. Since each burst
 * have a different set of parameters (number of pulse, pri) than
 * the previous one we only use pulse width to add the pulse in the
 * sequence.
 * We only queue one pulse per burst and valid the radar when enough burst
 * has been detected.
 */
static
struct pri_sequence *pde_long_add_pulse(struct pri_detector *pde,
										u16 len, u64 ts, u16 pri)
{
	struct pri_sequence *ps;
	const struct radar_detector_specs *rs = pde->rs;

	if (list_empty(&pde->sequences)) {
		/* First pulse, create a new sequence */
		ps = pool_get_pseq_elem();
		if (ps == NULL) {
			return NULL;
		}

		/*For long waveform, "count" represents the number of burst detected */
		ps->count = 1;
		/*"count_false" represents the number of pulse in the current burst */
		ps->count_falses = 1;
		ps->first_ts = ts;
		ps->last_ts = ts;
		ps->deadline_ts = ts + pde->window_size;
		ps->pri = 0;
		INIT_LIST_HEAD(&ps->head);
		list_add(&ps->head, &pde->sequences);
		pulse_queue_enqueue(pde, ts);
	} else {
		u32 delta_ts;

		ps = (struct pri_sequence *)pde->sequences.next;

		delta_ts = ts - ps->last_ts;
		ps->last_ts = ts;

		if (delta_ts < rs->pri_max) {
			/* ignore pulse too close from previous one */
		} else if  ((delta_ts >= rs->pri_min) &&
			  (delta_ts <= rs->pri_max)) {
			/* this is a new pulse in the current burst, ignore it
			   (i.e don't queue it) */
			ps->count_falses++;
		} else if ((ps->count > 2) &&
				   (ps->dur + delta_ts) < LONG_RADAR_BURST_MIN_DURATION) {
			/* not enough time between burst, ignore pulse */
		} else {
			/* a new burst */
			ps->count++;
			ps->count_falses = 1;

			/* reset the start of the sequence if deadline reached */
			if (ts > ps->deadline_ts) {
				struct pulse_elem *p;
				u64 min_valid_ts;

				min_valid_ts = ts - pde->window_size;
				while ((p = pulse_queue_get_tail(pde)) != NULL) {
					if (p->ts >= min_valid_ts) {
						ps->first_ts = p->ts;
						ps->deadline_ts = p->ts + pde->window_size;
						break;
					}
					pulse_queue_dequeue(pde);
					ps->count--;
				}
			}

			/* valid radar if enough burst detected and delta with first burst
			   is at least duration/2 */
			if (ps->count > pde->rs->ppb_thresh &&
				(ts - ps->first_ts) > (pde->window_size / 2)) {
				return ps;
			} else {
				pulse_queue_enqueue(pde, ts);
				ps->dur = delta_ts;
			}
		}
	}

	return NULL;
}

/**
 * pri detector ops to detect long radar waveform
 */
static struct pri_detector_ops pri_detector_long = {
	.init = pde_long_init,
	.add_pulse = pde_long_add_pulse,
	.reset_on_pri_overflow = 0,
};


/***************************************************************************
 * PRI detector init/reset/exit/get
 **************************************************************************/
/**
 * pri_detector_init- Create a new pri_detector
 *
 * @dpd: dfs_pattern_detector instance pointer
 * @radar_type: index of radar pattern
 * @freq: Frequency of the pri detector
 */
struct pri_detector *pri_detector_init(struct dfs_pattern_detector *dpd,
									   u16 radar_type, u16 freq)
{
	struct pri_detector *pde;

	pde = kzalloc(sizeof(*pde), GFP_ATOMIC);
	if (pde == NULL)
		return NULL;

	INIT_LIST_HEAD(&pde->sequences);
	INIT_LIST_HEAD(&pde->pulses);
	INIT_LIST_HEAD(&pde->head);
	list_add(&pde->head, &dpd->detectors[radar_type]);

	pde->rs = &dpd->radar_spec[radar_type];
	pde->freq = freq;

	if (pde->rs->type == RADAR_WAVEFORM_LONG) {
		/* for LONG WAVEFORM */
		pde->ops = &pri_detector_long;
	} else {
		/* for SHORT, WEATHER and INTERLEAVED */
		pde->ops = &pri_detector_short;
	}

	/* Init dependent of specs */
	pde->ops->init(pde);

	pool_register_ref();
	return pde;
}

/**
 * pri_detector_reset - Reset pri_detector
 *
 * @pde: pointer on pri_detector
 * @ts: New ts reference for the pri_detector
 *
 * free pulse queue and sequences list and give objects back to pools
 */
static
void pri_detector_reset(struct pri_detector *pde, u64 ts)
{
	struct pri_sequence *ps, *ps0;
	struct pulse_elem *p, *p0;
	list_for_each_entry_safe(ps, ps0, &pde->sequences, head) {
		list_del_init(&ps->head);
		pool_put_pseq_elem(ps);
	}
	list_for_each_entry_safe(p, p0, &pde->pulses, head) {
		list_del_init(&p->head);
		pool_put_pulse_elem(p);
	}
	pde->count = 0;
	pde->last_ts = ts;
}

/**
 *  pri_detector_exit - Delete pri_detector
 *
 *  @pde: pointer on pri_detector
 */
static
void pri_detector_exit(struct pri_detector *pde)
{
	pri_detector_reset(pde, 0);
	pool_deregister_ref();
	list_del(&pde->head);
	kfree(pde);
}

/**
 * pri_detector_get() - get pri detector for a given frequency and type
 * @dpd: dfs_pattern_detector instance pointer
 * @freq: frequency in MHz
 * @radar_type: index of radar pattern
 * @return pointer to pri detector on success, NULL otherwise
 *
 * Return existing pri detector for the given frequency or return a
 * newly create one.
 * Pri detector are "merged" by frequency so that if a pri detector for a freq
 * of +/- 2Mhz already exists don't create a new one.
 *
 * Maybe will need to adapt frequency merge for pattern with chirp.
 */
static struct pri_detector *
pri_detector_get(struct dfs_pattern_detector *dpd, u16 freq, u16 radar_type)
{
	struct pri_detector *pde, *cur = NULL;
	list_for_each_entry(pde, &dpd->detectors[radar_type], head) {
		if (pde->freq == freq) {
			if (pde->count)
				return pde;
			else
				cur = pde;
		} else if (pde->freq - 2 == freq && pde->count) {
			return pde;
		} else if (pde->freq + 2 == freq && pde->count) {
			return pde;
		}
	}

	if (cur)
		return cur;
	else
		return pri_detector_init(dpd, radar_type, freq);
}


/******************************************************************************
 * DFS Pattern Detector
 *****************************************************************************/
/**
 * dfs_pattern_detector_reset() - reset all channel detectors
 *
 * @dpd: dfs_pattern_detector
 */
static void dfs_pattern_detector_reset(struct dfs_pattern_detector *dpd)
{
	struct pri_detector *pde;
	int i;

	for (i = 0; i < dpd->num_radar_types; i++) {
		if (!list_empty(&dpd->detectors[i]))
			list_for_each_entry(pde, &dpd->detectors[i], head)
				pri_detector_reset(pde, dpd->last_pulse_ts);
	}

	dpd->last_pulse_ts = 0;
	dpd->prev_jiffies = jiffies;
}

/**
 * dfs_pattern_detector_reset() - delete all channel detectors
 *
 * @dpd: dfs_pattern_detector
 */
static void dfs_pattern_detector_exit(struct dfs_pattern_detector *dpd)
{
	struct pri_detector *pde, *pde0;
	int i;

	for (i = 0; i < dpd->num_radar_types; i++) {
		if (!list_empty(&dpd->detectors[i]))
			list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
				pri_detector_exit(pde);
	}

	kfree(dpd);
}

/**
 * dfs_pattern_detector_pri_overflow - reset all channel detectors on pri
 *                                     overflow
 * @dpd: dfs_pattern_detector
 */
static void dfs_pattern_detector_pri_overflow(struct dfs_pattern_detector *dpd)
{
	struct pri_detector *pde;
	int i;

	for (i = 0; i < dpd->num_radar_types; i++) {
		if (!list_empty(&dpd->detectors[i]))
			list_for_each_entry(pde, &dpd->detectors[i], head)
				if (pde->ops->reset_on_pri_overflow)
					pri_detector_reset(pde, dpd->last_pulse_ts);
	}
}

/**
 * dfs_pattern_detector_add_pulse - Process one pulse
 *
 * @dpd: dfs_pattern_detector
 * @chain: Chain that correspond to this pattern_detector (only for debug)
 * @freq: frequency of the pulse
 * @pri: Delta with previous pulse. (0 if delta is too big for u16)
 * @len: width of the pulse
 * @now: jiffies value when pulse was received
 *
 * Get (or create) the channel_detector for this frequency. Then add the pulse
 * in each pri_detector created in this channel_detector.
 *
 *
 * @return True is the pulse complete a radar pattern, false otherwise
 */
static bool dfs_pattern_detector_add_pulse(struct dfs_pattern_detector *dpd,
										   enum rwnx_radar_chain chain,
										   u16 freq, u16 pri, u16 len, u32 now)
{
	u32 i;

	/*
	 * pulses received for a non-supported or un-initialized
	 * domain are treated as detected radars for fail-safety
	 */
	if (dpd->region == NL80211_DFS_UNSET)
		return true;

	/* Compute pulse time stamp */
	if (pri == 0) {
		u32 delta_jiffie;
		if (unlikely(now < dpd->prev_jiffies)) {
			delta_jiffie = 0xffffffff - dpd->prev_jiffies + now;
		} else {
			delta_jiffie = now - dpd->prev_jiffies;
		}
		dpd->last_pulse_ts += jiffies_to_usecs(delta_jiffie);
		dpd->prev_jiffies = now;
		dfs_pattern_detector_pri_overflow(dpd);
	} else {
		dpd->last_pulse_ts += pri;
	}

	for (i = 0; i < dpd->num_radar_types; i++) {
		struct pri_sequence *ps;
		struct pri_detector *pde;
		const struct radar_detector_specs *rs = &dpd->radar_spec[i];

		/* no need to look up for pde if len is not within range */
		if ((rs->width_min > len) ||
			(rs->width_max < len)) {
			continue;
		}

		pde = pri_detector_get(dpd, freq, i);
		ps = pde->ops->add_pulse(pde, len, dpd->last_pulse_ts, pri);

		if (ps != NULL) {
#ifdef CREATE_TRACE_POINTS
			trace_radar_detected(chain, dpd->region, pde->freq, i, ps->pri);
#endif
			// reset everything instead of just the channel detector
			dfs_pattern_detector_reset(dpd);
			return true;
		}
	}

	return false;
}

/**
 * get_dfs_domain_radar_types() - get radar types for a given DFS domain
 * @param domain DFS domain
 * @return radar_types ptr on success, NULL if DFS domain is not supported
 */
static const struct radar_types *
get_dfs_domain_radar_types(enum nl80211_dfs_regions region)
{
	u32 i;
	for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
		if (dfs_domains[i]->region == region)
			return dfs_domains[i];
	}
	return NULL;
}

/**
 * get_dfs_max_radar_types() - get maximum radar types for all supported domain
 * @return the maximum number of radar pattern supported by on region
 */
static u16 get_dfs_max_radar_types(void)
{
	u32 i;
	u16 max = 0;
	for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) {
		if (dfs_domains[i]->num_radar_types > max)
			max = dfs_domains[i]->num_radar_types;
	}
	return max;
}

/**
 * dfs_pattern_detector_set_domain - set DFS domain
 *
 * @dpd: dfs_pattern_detector
 * @region: DFS region
 *
 * set DFS domain, resets detector lines upon domain changes
 */
static
bool dfs_pattern_detector_set_domain(struct dfs_pattern_detector *dpd,
									 enum nl80211_dfs_regions region, u8 chain)
{
	const struct radar_types *rt;
	struct pri_detector *pde, *pde0;
	int i;

	if (dpd->region == region)
		return true;

	dpd->region = NL80211_DFS_UNSET;

	rt = get_dfs_domain_radar_types(region);
	if (rt == NULL)
		return false;

	/* delete all pri detectors for previous DFS domain */
	for (i = 0; i < dpd->num_radar_types; i++) {
		if (!list_empty(&dpd->detectors[i]))
			list_for_each_entry_safe(pde, pde0, &dpd->detectors[i], head)
				pri_detector_exit(pde);
	}

	if (chain == RWNX_RADAR_RIU)
		dpd->radar_spec = rt->spec_riu;
	else
		dpd->radar_spec = rt->spec_fcu;
	dpd->num_radar_types = rt->num_radar_types;

	dpd->region = region;
	return true;
}

/**
 * dfs_pattern_detector_init - Initialize dfs_pattern_detector
 *
 * @region: DFS region
 * @return: pointer on dfs_pattern_detector
 *
 */
static struct dfs_pattern_detector *
dfs_pattern_detector_init(enum nl80211_dfs_regions region, u8 chain)
{
	struct dfs_pattern_detector *dpd;
	u16 i, max_radar_type = get_dfs_max_radar_types();

	dpd = kmalloc(sizeof(*dpd) + max_radar_type * sizeof(dpd->detectors[0]),
				  GFP_KERNEL);
	if (dpd == NULL)
		return NULL;

	dpd->region = NL80211_DFS_UNSET;
	dpd->enabled = RWNX_RADAR_DETECT_DISABLE;
	dpd->last_pulse_ts = 0;
	dpd->prev_jiffies = jiffies;
	dpd->num_radar_types = 0;
	for (i = 0; i < max_radar_type; i++)
		INIT_LIST_HEAD(&dpd->detectors[i]);

	if (dfs_pattern_detector_set_domain(dpd, region, chain))
		return dpd;

	kfree(dpd);
	return NULL;
}


/******************************************************************************
 * driver interface
 *****************************************************************************/
static u16 rwnx_radar_get_center_freq(struct rwnx_hw *rwnx_hw, u8 chain)
{
	if (chain == RWNX_RADAR_FCU)
		return rwnx_hw->phy.sec_chan.center_freq1;

	if (chain == RWNX_RADAR_RIU) {
#ifdef CONFIG_RWNX_FULLMAC
		if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
			WARN(1, "Radar pulse without channel information");
		} else
			return rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def.center_freq1;
#endif /* CONFIG_RWNX_FULLMAC */
	}

	return 0;
}

static void rwnx_radar_detected(struct rwnx_hw *rwnx_hw)
{
#ifdef CONFIG_RWNX_FULLMAC
	struct cfg80211_chan_def chan_def;

	if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx)) {
		WARN(1, "Radar detected without channel information");
		return;
	}

	/*
	  recopy chan_def in local variable because rwnx_radar_cancel_cac may
	  clean the variable (if in CAC and it's the only vif using this context)
	  and CAC should be aborted before reporting the radar.
	*/
	chan_def = rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx].chan_def;

	rwnx_radar_cancel_cac(&rwnx_hw->radar);
	cfg80211_radar_event(rwnx_hw->wiphy, &chan_def, GFP_KERNEL);

#endif /* CONFIG_RWNX_FULLMAC */
}

static void rwnx_radar_process_pulse(struct work_struct *ws)
{
	struct rwnx_radar *radar = container_of(ws, struct rwnx_radar,
											detection_work);
	struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
	int chain;
	u32 pulses[RWNX_RADAR_LAST][RWNX_RADAR_PULSE_MAX];
	u16 pulses_count[RWNX_RADAR_LAST];
	u32 now = jiffies; /* would be better to store jiffies value in IT handler */

	/* recopy pulses locally to avoid too long spin_lock */
	spin_lock_bh(&radar->lock);
	for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
		int start, count;

		count = radar->pulses[chain].count;
		start = radar->pulses[chain].index - count;
		if (start < 0)
			start += RWNX_RADAR_PULSE_MAX;

		pulses_count[chain] = count;
		if (count == 0)
			continue;

		if ((start + count) > RWNX_RADAR_PULSE_MAX) {
			u16 count1 = (RWNX_RADAR_PULSE_MAX - start);
			memcpy(&(pulses[chain][0]),
				   &(radar->pulses[chain].buffer[start]),
				   count1 * sizeof(struct radar_pulse));
			memcpy(&(pulses[chain][count1]),
				   &(radar->pulses[chain].buffer[0]),
				   (count - count1) * sizeof(struct radar_pulse));
		} else {
			memcpy(&(pulses[chain][0]),
				   &(radar->pulses[chain].buffer[start]),
				   count * sizeof(struct radar_pulse));
		}
		radar->pulses[chain].count = 0;
	}
	spin_unlock_bh(&radar->lock);


	/* now process pulses */
	for (chain = RWNX_RADAR_RIU; chain < RWNX_RADAR_LAST; chain++) {
		int i;
		u16 freq;

		if (pulses_count[chain] == 0)
			continue;

		freq = rwnx_radar_get_center_freq(rwnx_hw, chain);

		for (i = 0; i < pulses_count[chain] ; i++) {
			struct radar_pulse *p = (struct radar_pulse *)&pulses[chain][i];
#ifdef CREATE_TRACE_POINTS
			trace_radar_pulse(chain, p);
#endif
			if (dfs_pattern_detector_add_pulse(radar->dpd[chain], chain,
											   (s16)freq + (2 * p->freq),
											   p->rep, (p->len * 2), now)) {
				u16 idx = radar->detected[chain].index;

				if (chain == RWNX_RADAR_RIU) {
					/* operating chain, inform upper layer to change channel */
					if (radar->dpd[chain]->enabled == RWNX_RADAR_DETECT_REPORT) {
						rwnx_radar_detected(rwnx_hw);
						/* no need to report new radar until upper layer set a
						   new channel. This prevent warning if a new radar is
						   detected while mac80211 is changing channel */
						rwnx_radar_detection_enable(radar,
													RWNX_RADAR_DETECT_DISABLE,
													chain);
						/* purge any event received since the beginning of the
						   function (we are sure not to interfer with tasklet
						   as we disable detection just before) */
						radar->pulses[chain].count = 0;
					}
				} else {
					/* secondary radar detection chain, simply report info in
					   debugfs for now */
				}

				radar->detected[chain].freq[idx] = (s16)freq + (2 * p->freq);
				radar->detected[chain].time[idx] = ktime_get_real_seconds();
				radar->detected[chain].index = ((idx + 1) %
												NX_NB_RADAR_DETECTED);
				radar->detected[chain].count++;
				/* no need to process next pulses for this chain */
				break;
			 }
		}
	}
}

#ifdef CONFIG_RWNX_FULLMAC
static void rwnx_radar_cac_work(struct work_struct *ws)
{
	struct delayed_work *dw = container_of(ws, struct delayed_work, work);
	struct rwnx_radar *radar = container_of(dw, struct rwnx_radar, cac_work);
	struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);
	struct rwnx_chanctx *ctxt;

	if (radar->cac_vif == NULL) {
		WARN(1, "CAC finished but no vif set");
		return;
	}

	ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
	cfg80211_cac_event(radar->cac_vif->ndev,
					#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
					   &ctxt->chan_def,
					#endif
					   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
	rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
	rwnx_chanctx_unlink(radar->cac_vif);

	radar->cac_vif = NULL;
}
#endif /* CONFIG_RWNX_FULLMAC */

bool rwnx_radar_detection_init(struct rwnx_radar *radar)
{
	spin_lock_init(&radar->lock);

	radar->dpd[RWNX_RADAR_RIU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
														   RWNX_RADAR_RIU);
	if (radar->dpd[RWNX_RADAR_RIU] == NULL)
		return false;

	radar->dpd[RWNX_RADAR_FCU] = dfs_pattern_detector_init(NL80211_DFS_UNSET,
														   RWNX_RADAR_FCU);
	if (radar->dpd[RWNX_RADAR_FCU] == NULL) {
		rwnx_radar_detection_deinit(radar);
		return false;
	}

	INIT_WORK(&radar->detection_work, rwnx_radar_process_pulse);
#ifdef CONFIG_RWNX_FULLMAC
	INIT_DELAYED_WORK(&radar->cac_work, rwnx_radar_cac_work);
	radar->cac_vif = NULL;
#endif /* CONFIG_RWNX_FULLMAC */
	return true;
}

void rwnx_radar_detection_deinit(struct rwnx_radar *radar)
{
	if (radar->dpd[RWNX_RADAR_RIU]) {
		dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_RIU]);
		radar->dpd[RWNX_RADAR_RIU] = NULL;
	}
	if (radar->dpd[RWNX_RADAR_FCU]) {
		dfs_pattern_detector_exit(radar->dpd[RWNX_RADAR_FCU]);
		radar->dpd[RWNX_RADAR_FCU] = NULL;
	}
}

bool rwnx_radar_set_domain(struct rwnx_radar *radar,
						   enum nl80211_dfs_regions region)
{
	if (radar->dpd[0] == NULL)
		return false;
#ifdef CREATE_TRACE_POINTS
	trace_radar_set_region(region);
#endif
	return (dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_RIU],
											region, RWNX_RADAR_RIU) &&
			dfs_pattern_detector_set_domain(radar->dpd[RWNX_RADAR_FCU],
											region, RWNX_RADAR_FCU));
}

void rwnx_radar_detection_enable(struct rwnx_radar *radar, u8 enable, u8 chain)
{
	if (chain < RWNX_RADAR_LAST) {
#ifdef CREATE_TRACE_POINTS
		trace_radar_enable_detection(radar->dpd[chain]->region, enable, chain);
#endif
		spin_lock_bh(&radar->lock);
		radar->dpd[chain]->enabled = enable;
		spin_unlock_bh(&radar->lock);
	}
}

bool rwnx_radar_detection_is_enable(struct rwnx_radar *radar, u8 chain)
{
	return radar->dpd[chain]->enabled != RWNX_RADAR_DETECT_DISABLE;
}

#ifdef CONFIG_RWNX_FULLMAC
void rwnx_radar_start_cac(struct rwnx_radar *radar, u32 cac_time_ms,
						  struct rwnx_vif *vif)
{
	WARN(radar->cac_vif != NULL, "CAC already in progress");
	radar->cac_vif = vif;
	schedule_delayed_work(&radar->cac_work, msecs_to_jiffies(cac_time_ms));
}

void rwnx_radar_cancel_cac(struct rwnx_radar *radar)
{
	struct rwnx_hw *rwnx_hw = container_of(radar, struct rwnx_hw, radar);

	if (radar->cac_vif == NULL) {
		return;
	}

	if (cancel_delayed_work(&radar->cac_work)) {
		struct rwnx_chanctx *ctxt;
		ctxt = &rwnx_hw->chanctx_table[radar->cac_vif->ch_index];
		rwnx_send_apm_stop_cac_req(rwnx_hw, radar->cac_vif);
		cfg80211_cac_event(radar->cac_vif->ndev,
						#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
						   &ctxt->chan_def,
						#endif
						   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
		rwnx_chanctx_unlink(radar->cac_vif);
	}

	radar->cac_vif = NULL;
}

void rwnx_radar_detection_enable_on_cur_channel(struct rwnx_hw *rwnx_hw)
{
	struct rwnx_chanctx *ctxt;

	/* If no information on current channel do nothing */
	if (!rwnx_chanctx_valid(rwnx_hw, rwnx_hw->cur_chanctx))
		return;

	ctxt = &rwnx_hw->chanctx_table[rwnx_hw->cur_chanctx];
	if (ctxt->chan_def.chan->flags & IEEE80211_CHAN_RADAR) {
		rwnx_radar_detection_enable(&rwnx_hw->radar,
									RWNX_RADAR_DETECT_REPORT,
									RWNX_RADAR_RIU);
	} else {
		rwnx_radar_detection_enable(&rwnx_hw->radar,
									RWNX_RADAR_DETECT_DISABLE,
									RWNX_RADAR_RIU);
	}
}
#endif /* CONFIG_RWNX_FULLMAC */

/*****************************************************************************
 * Debug functions
 *****************************************************************************/
static
int rwnx_radar_dump_pri_detector(char *buf, size_t len,
								 struct pri_detector *pde)
{
	char freq_info[] = "Freq = %3.dMhz\n";
	char seq_info[] = " pri    | count | false \n";
	struct pri_sequence *seq;
	int res, write = 0;

	if (list_empty(&pde->sequences)) {
		return 0;
	}

	if (buf == NULL) {
		int nb_seq = 1;
		list_for_each_entry(seq, &pde->sequences, head) {
			nb_seq++;
		}

		return (sizeof(freq_info) + nb_seq * sizeof(seq_info));
	}

	res = scnprintf(buf, len, freq_info, pde->freq);
	write += res;
	len -= res;

	res = scnprintf(&buf[write], len, "%s", seq_info);
	write += res;
	len -= res;

	list_for_each_entry(seq, &pde->sequences, head) {
		res = scnprintf(&buf[write], len, " %6.d |   %2.d  |    %.2d \n",
						seq->pri, seq->count, seq->count_falses);
		write += res;
		len -= res;
	}

	return write;
}

int rwnx_radar_dump_pattern_detector(char *buf, size_t len,
									 struct rwnx_radar *radar, u8 chain)
{
	struct dfs_pattern_detector *dpd = radar->dpd[chain];
	char info[] = "Type = %3.d\n";
	struct pri_detector *pde;
	int i, res, write = 0;

	/* if buf is NULL return size needed for dump */
	if (buf == NULL) {
		int size_needed = 0;

		for (i = 0; i < dpd->num_radar_types; i++) {
			list_for_each_entry(pde, &dpd->detectors[i], head) {
				size_needed += rwnx_radar_dump_pri_detector(NULL, 0, pde);
			}
			size_needed += sizeof(info);

		return size_needed;
		}
	}

	/* */
	for (i = 0; i < dpd->num_radar_types; i++) {
		res = scnprintf(&buf[write], len, info, i);

		write += res;
		len -= res;
		list_for_each_entry(pde, &dpd->detectors[i], head) {
			res = rwnx_radar_dump_pri_detector(&buf[write], len, pde);
			write += res;
			len -= res;
		}
	}

	return write;
}


int rwnx_radar_dump_radar_detected(char *buf, size_t len,
								   struct rwnx_radar *radar, u8 chain)
{
	struct rwnx_radar_detected *detect = &(radar->detected[chain]);
	char info[] = "2001/02/02 - 02:20 5126MHz\n";
	int idx, i, res, write = 0;
	int count = detect->count;

	if (count > NX_NB_RADAR_DETECTED)
		count = NX_NB_RADAR_DETECTED;

	if (buf == NULL) {
		return (count * sizeof(info)) + 1;
	 }

	idx = (detect->index - detect->count) % NX_NB_RADAR_DETECTED;

	for (i = 0; i < count; i++) {
		struct tm tm;
		time64_to_tm(detect->time[idx], 0, &tm);

		res = scnprintf(&buf[write], len,
						"%.4d/%.2d/%.2d - %.2d:%.2d %4.4dMHz\n",
						(int)tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
						tm.tm_hour, tm.tm_min, detect->freq[idx]);
		write += res;
		len -= res;

		idx = (idx + 1) % NX_NB_RADAR_DETECTED;
	}

	return write;
}
